﻿using System;
using System.Collections.Generic;
using System.Text;
using PersistentManager.Mapping;
using System.Reflection;
using PersistentManager.Metadata;
using System.Linq;

namespace PersistentManager.Descriptors
{
    public class PropertyMetadata : ICloneable
    {
        private string fieldName;
        private string entityFieldName;
        private Type propertyType;
        private object fieldValue;
        private bool autoKey = false;
        private long length;
        private bool allowNull;
        private bool isUniqueIdentifier;
        private string dbType;
        private bool _isManyToOne;
        private string joinTable;
        private bool _isOneToOne;
        private bool _isOneToMany;
        private Type relationType;
        private bool _isManyToMany;
        private Cascade cascade;
        private bool isDiscriminator;
        private bool isImported = false;
        private bool isInheritance = false;
        private Type declaringType;
        private object excludeValue = null;
        private IList<JoinMetadata> joinDetails;

        internal PropertyMetadata()
        {
            joinDetails = new List<JoinMetadata>();
        }

        public Guid CompositeId { get; set; }

        public bool IsCompositional { get; set; }

        public Type DeclaringType
        {
            get { return declaringType; }
            set { declaringType = value; }
        }

        public bool IsInheritance
        {
            get { return isInheritance; }
            set { isInheritance = value; }
        }

        public bool IsImported
        {
            get { return isImported; }
            set { isImported = value; }
        }

        public string MappingName
        {
            get { return fieldName; }
            set { fieldName = value; }
        }

        public string ClassDefinationName
        {
            get { return entityFieldName; }
            set { entityFieldName = value; }
        }

        public Type EntityPropertyType { get; set; }

        public Type JoinTableType { get; set; }

        public bool IsEntitySplitJoin { get; set; }

        public Type PropertyType
        {
            get { return propertyType; }
            set { propertyType = value; }
        }                

        public bool IsAutoGenerated
        {
            get { return autoKey; }
            set { autoKey = value; }
        }

        public object FieldValue
        {
            get { return fieldValue; }
            set
            {
                if (value == null)
                {
                    fieldValue = System.DBNull.Value;
                }
                else
                {
                    fieldValue = value;
                }
            }
        }

        public long Length
        {
            get { return length; }
            set { length = value; }
        }        

        public bool AllowNull
        {
            get { return allowNull; }
            set { allowNull = value; }
        }

        public bool IsUniqueIdentifier
        {
            get { return isUniqueIdentifier; }
            set { isUniqueIdentifier = value; }
        }        

        public string DbType
        {
            get { return dbType; }
            set { dbType = value; }
        }

        public bool IsManyToOne
        {
            get { return _isManyToOne; }
            set { _isManyToOne = value; }
        }      

        public bool IsOneToOne
        {
            get { return _isOneToOne; }
            set { _isOneToOne = value; }
        }

        public bool IsOneToMany
        {
            get { return _isOneToMany; }
            set { _isOneToMany = value; }
        }

        public bool IsManyToMany
        {
            get { return _isManyToMany; }
            set { _isManyToMany = value; }
        }

        public bool IsRelationshipMapping
        {
            get { return ( IsManyToMany || IsManyToOne || IsOneToMany || IsOneToOne ); }
        }

        public bool IsOneSided
        {
            get { return (IsManyToOne || IsOneToOne); }
        }

        public bool IsManySided
        {
            get { return ( IsManyToMany || IsOneToMany ); }
        }

        public string JoinTable
        {
            get { return joinTable; }
            set { joinTable = value; }
        }

        public bool IsPlaceHolding { get; set; }

        public Type RelationType
        {
            get { return relationType; }
            set { relationType = value; }
        }
        
        public object ExcludeValue
        {
            get { return excludeValue; }
            set { excludeValue = value; }
        }

        public bool HasExcludedValue
        {
            get { return ExcludeValue.IsNotNull(); }
        }

        public int KeyPriority { get; set; }

        public Cascade Cascade
        {
            get { return cascade; }
            set { cascade = value; }
        }

        public bool IsDiscriminator
        {
            get { return isDiscriminator; }
            set { isDiscriminator = value; }
        }

        public bool IsMapped( PropertyInfo propertyInfo )
        {
            if ( propertyInfo.Name == ClassDefinationName )
            {
                return true;
            }

            return false;
        }

        public object Clone()
        {
            PropertyMetadata propertyMetadata = this.MemberwiseClone() as PropertyMetadata;
            propertyMetadata.JoinDetails = new List<JoinMetadata>();

            foreach ( JoinMetadata join in this.JoinDetails )
            {
                propertyMetadata.JoinDetails.Add( join.Clone() as JoinMetadata );
            }

            return propertyMetadata;
        }

        public RelationshipType RelationshipType
        {
            get
            {
                if ( IsOneToMany )
                    return RelationshipType.OneToMany;
                else if ( IsManyToMany )
                    return RelationshipType.ManyToMany;


                return RelationshipType.OneToOne;
            }
        }

        public void AddJoin( JoinMetadata join )
        {
            joinDetails.Add( join );
        }

        public void AddJoin( string relationColumn , string joinColumn , Type columnType )
        {
            joinDetails.Add( new JoinMetadata( relationColumn , joinColumn , columnType ) );
        }

        public IList<JoinMetadata> JoinDetails
        {
            get { return joinDetails; }
            set { joinDetails = value; }
        }

        internal string[] RelationColumns
        {
            get
            {
                return JoinDetails.Select( j => j.RelationColumn ).ToArray( );
            }
        }

        internal string[] JoinColumns
        {
            get
            {
                return JoinDetails.Select( j => j.JoinColumn ).ToArray( );
            }
        }

        internal string[] OwningColumns
        {
            get
            {
                return JoinDetails.Select( j => j.OwnerColumn ).ToArray( );
            }
        }

        internal Type[] ColumnTypes
        {
            get
            {
                return JoinDetails.Select( j => j.ColumnType ).ToArray( );
            }
        }

        internal string[] RightKeys
        {
            get
            {
                return JoinDetails.Select(j => j.RightKey ).ToArray();
            }
        }

        internal string[] LeftKeys
        {
            get
            {
                return JoinDetails.Select(j => j.LeftKey ).ToArray();
            }
        }
    }
}
